package xs.szw.service.service.impl;


import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.collect.ImmutableList;
import lombok.RequiredArgsConstructor;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.transaction.annotation.Transactional;
import xs.szw.common.constant.RedisKeyConstant;
import xs.szw.common.exception.BusinessException;
import xs.szw.common.model.PageResult;
import xs.szw.service.model.dto.user.*;
import xs.szw.service.model.vo.UserInfoVo;
import xs.szw.service.model.vo.UserVo;
import xs.szw.service.converter.UserConverter;
import xs.szw.service.enums.SmsTypeEnum;
import xs.szw.service.mapper.UserRoleMapper;
import xs.szw.service.model.pojo.security.LoginUser;
import xs.szw.service.model.pojo.security.UserAuthInfo;
import xs.szw.service.model.pojo.sys.User;
import xs.szw.service.model.pojo.sys.UserRole;
import xs.szw.service.service.RoleService;
import xs.szw.service.service.UserService;
import xs.szw.service.mapper.UserMapper;
import org.springframework.stereotype.Service;
import xs.szw.service.service.mail.MailService;
import xs.szw.service.service.sercurity.PermissionService;
import xs.szw.service.service.sms.SmsService;
import xs.szw.service.utils.RedisCache;
import xs.szw.service.utils.SecurityUtils;

import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
* @author asus
* @description 针对表【sys_user(用户信息表)】的数据库操作Service实现
* @createDate 2025-02-10 18:35:53
*/
@Service
@RequiredArgsConstructor
public class UserServiceImpl extends ServiceImpl<UserMapper, User>
        implements UserService, UserDetailsService {

    
    private final UserMapper userMapper;

    
    private final UserRoleMapper userRoleMapper;
    
    private final PasswordEncoder passwordEncoder;

    
    private final RoleService roleService;
    
    private final UserConverter userConverter;

    
    private final PermissionService permissionService;

    
    private final RedisCache redisCache;

    private final SmsService smsService;

    private final MailService mailService;

    /**
     * 根据用户名查询用户信息
     * @param username
     * @return
     * @throws UsernameNotFoundException
     */
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        //根据用户名查询用户信息
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(User::getUsername,username);
        User user = userMapper.selectOne(wrapper);
        System.out.println("loadUserByUsername");
        //如果查询不到数据就通过抛出异常来给出提示
        if(Objects.isNull(user)){
            throw new RuntimeException("用户名或密码错误");
        }

        //TODO 根据用户查询权限信息 添加到LoginUser中
//        List<String> stringList = menuMapper.selectPermsByUserId(user.getId());
        UserAuthInfo userAuthInfo = getUserAuthInfo(username);
        ImmutableList.Builder<String> builder = ImmutableList.builder();
        builder.add("hello");
        builder.add("lihao");

        //角色编码code集合
        List<String> stringList = new ArrayList<>(userAuthInfo.getRoles());
//        List<String> list = CollectionUtil.newArrayList(userAuthInfo.getRoles());
        //封装成UserDetails对象返回
        return new LoginUser(user, stringList);
    }

    /**
     * 根据用户名获取认证信息
     * @param username 用户名
     * @return
     */
    @Override
    public UserAuthInfo getUserAuthInfo(String username) {
        UserAuthInfo userAuthInfo = userMapper.getUserAuthInfo(username);
        //获取角色编码set集合
        if(userAuthInfo != null){
            Set<String> roles = userAuthInfo.getRoles();
            //获取最大范围的数据权限
            Integer dataScope = roleService.getMaximumDataScope(roles);
            userAuthInfo.setDataScope(dataScope);
        }
        return userAuthInfo;
    }

    /**
     * 修改密码
     * @param currUserId
     * @param passwordUpdateDto
     * @return
     */
    @Override
    public boolean updatePassword(Long currUserId, PasswordUpdateDto passwordUpdateDto) {
        User user = this.getById(currUserId);
        if (user == null) {
            throw new BusinessException("用户不存在");
        }

        //1.校验原密码是否正确
        String oldPassword = passwordUpdateDto.getOldPassword();
        if (!passwordEncoder.matches(oldPassword, user.getPassword())) {
            throw new BusinessException("原密码不正确!");
        }
        //2.检验新密码
        String newPassword = passwordUpdateDto.getNewPassword();
        if(passwordEncoder.matches(newPassword, user.getPassword())){
            throw new BusinessException("新密码不能与原密码相同");
        }
        user.setPassword(passwordEncoder.encode(newPassword));
        boolean b = this.updateById(user);
        //4.重新登录
        if(b){
            Long userId = SecurityUtils.getLoginUserId();
            redisCache.deleteObject(RedisKeyConstant.RBAC_Login_KEY +userId);
        }
        return b;
    }

    @Override
    public boolean resetPassword(Long userId, String password) {
        //1.判断用户是否存在
        User user = this.getById(userId);
        if (user == null) {
            throw new BusinessException("用户不存在");
        }
        //2.重置密码
        user.setPassword(passwordEncoder.encode(password));
        return this.updateById(user);
    }

    @Override
    public boolean senMobileCode(String mobile) {
        //1.生成验证码
        //2.redis存储验证码
        //3.发送手机验证码
        // String code = String.valueOf((int) ((Math.random() * 9 + 1) * 1000));
        // TODO 为了方便测试，验证码固定为 1234，实际开发中在配置了厂商短信服务后，可以使用上面的随机验证码
        String code = "1234";

        Map<String, String> templateParams = new HashMap<>();
        templateParams.put("code", code);
        boolean result = smsService.sendSms(mobile, SmsTypeEnum.CHANGE_MOBILE, templateParams);
        if (result) {
            // 缓存验证码，5分钟有效，用于更换手机号校验
            String redisCacheKey = RedisKeyConstant.SMS_CHANGE_CODE_PREFIX + mobile;
            redisCache.setCacheObject(redisCacheKey, code, 5, TimeUnit.MINUTES);
        }
        return result;
    }

    /**
     * 绑定/更改手机号
     * @param mobileUpdateDto
     * @return
     */
    @Override
    public boolean bindMobileOrUpdateMobile(MobileUpdateDto mobileUpdateDto) {
        Long userId = SecurityUtils.getLoginUserId();
        User u = this.getById(userId);
        if (u == null){
            throw new BusinessException("用户不存在！");
        }
        validateUserMobileUnique(u.getId(), mobileUpdateDto.getMobile());
        //检验验证码
        String code = mobileUpdateDto.getCode();
        String mobile = mobileUpdateDto.getMobile();

        String key = RedisKeyConstant.SMS_CHANGE_CODE_PREFIX + mobile;
        String cacheCode = redisCache.getCacheObject(key);
        if (StrUtil.isBlank(cacheCode)){
            throw new BusinessException("验证码过期!");
        }
        if(!cacheCode.equals(code)){
            throw new BusinessException("验证码错误！");
        }
        //删除redis中验证码
        redisCache.deleteObject(key);
        return this.update(new LambdaUpdateWrapper<User>()
                .eq(User::getId,u.getId())
                .set(User::getMobile,mobile));
    }

    /**
     * 发送邮箱验证码（绑定或更换邮箱）
     * @param email
     */
    @Override
    public void sendEmailCode(String email) {

    }


    /**
     * 创建用户名
     * @param userDto
     * @return
     */
    @Override
    public boolean createUser(UserDto userDto) {
        //判断用户名是否唯一
        validateUserNameUnique(null, userDto.getUsername());
        User user = userConverter.userDtoToUser(userDto);
        //设置默认加密密码
        user.setPassword(passwordEncoder.encode(user.getPassword()));
        return this.save(user);
    }

    /**
     * 修改用户
     * @param userUpdateDto
     * @return
     */
    @Override
    public boolean updateUser(UserUpdateDto userUpdateDto) {
        //1.判断用户ID是否存在
        //2.判断用户名是否重复
        validateUserForCreateOrUpdate(userUpdateDto.getId(),userUpdateDto.getUsername(),userUpdateDto.getMobile());
        //执行sql
//        User user = userConverter.userDtoToUser(userDto);
        User user = userConverter.userUpdateDtoToUser(userUpdateDto);
       return this.updateById(user);
    }

    /**
     * 删除单个用户信息及其相关表记录信息
     * @param id
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean deleteUserWithRelations(Long id) {
        //判断用户ID是否存在
        validateUserExists(id);
        //删除用户表记录 用户角色表记录
        this.removeById(id);
        int delete = userRoleMapper.delete(new LambdaQueryWrapper<UserRole>().eq(UserRole::getUserId, id));
        return delete > 0 ? true : false;
    }

    /**
     * 批量删除用户信息及其相关记录表信息
     * @param ids
     * @return
     */
    @Override
    public boolean deleteBatchUserWithRelation(String ids) {
        //判空
        Assert.isTrue(!ids.isBlank(),"删除用户的ID数组为空");
        //批量删除
        List<Long> idList = Arrays.stream(ids.split(",")).map(Long::parseLong).collect(Collectors.toList());
        this.removeBatchByIds(idList);
        int delete = userRoleMapper.delete((new LambdaQueryWrapper<UserRole>().in(UserRole::getUserId, idList)));
        return delete > 0 ? true : false;
    }

    /**
     * 查询单个用户
     * @param id
     * @return
     */
    @Override
    public UserVo getUser(Long id) {
        //判断id是否空  是否存在
        validateUserExists(id);
        User u = this.getById(id);
        UserVo userVo = userConverter.userToUserVo(u);
        return userVo;
    }

    /**
     * 分页查询用户列表
     * @param userPageDto
     * @return
     */
    @Override
    public PageResult<UserVo> getUserPage(UserPageDto userPageDto) {
        int current = userPageDto.getPageNum();
        int size = userPageDto.getPageSize();
        //分页查询用户列表
        LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        Page<User> page = this.page(new Page<>(current, size), lambdaQueryWrapper);
        //user列表转换userVo
        List<UserVo> userVoList = userConverter.userListToUserVoList(page.getRecords());
        Page<UserVo> userPage = new Page<UserVo>(current, size, page.getTotal()).setRecords(userVoList);
        return new PageResult<>(userPage.getRecords(),userPage.getTotal());

    }

    /**
     * 获取当前登录用户的信息
     * @return
     */
    @Override
    public UserInfoVo getCurrentUserInfo() {
        //从SecurityContext中获取当前登录用户信息
        Long loginUserId = SecurityUtils.getLoginUserId();
        User u = this.getById(loginUserId);
        //实体类 -> vo
        UserInfoVo userInfoVo = userConverter.userToUserInfoVo(u);
        //从SecurityContextHolder中拿到当前用户的角色信息
        Set<String> roles = SecurityUtils.getRoles();
        userInfoVo.setRoles(roles);

        //查询用户的权限信息
        if(!CollectionUtil.isEmpty(roles)){
            Set<String> perms = permissionService.getRolePermsFromCache(roles);
            userInfoVo.setPerms(perms);
        }
        return userInfoVo;

    }



    private void validateUserForCreateOrUpdate(Long id, String name,String mobile) {
        // 校验自己存在
        validateUserExists(id);
        // 校验用户名的唯一性
        validateUserNameUnique(id, name);
        //校验用户名的唯一
        validateUserMobileUnique(id,mobile);
    }

    /**
     * 校验用户名唯一
     * @param id
     * @param name
     */
    private void validateUserNameUnique(Long id, String name) {
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(User::getUsername,name);
        User u = this.getOne(wrapper);
        if (u == null) {
            return;
        }
        // 如果 id 为空，说明不用比较是否为相同 id 的岗位
        //id==null 抛出异常
        Assert.notNull(id,"创建:用户名重复！");

        if(!u.getId().equals(id)){
            throw new BusinessException("更新:数据库已有用户名!");
        }
    }

    /**
     * 校验用户Id是否存在
     * @param id
     */
    private void validateUserExists(Long id) {
        if (id == null) {
            return;
        }

        if(this.getById(id)==null){
            throw new BusinessException("校验:用户不存在！");
        }
    }

    public void validateUserMobileUnique(Long id,String mobile){
        LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(User::getMobile,mobile);
        User user = this.getOne(lambdaQueryWrapper);
        if(user == null){
            return;
        }
        Assert.notNull(id,"创建:电话号重复！");

        if(!user.getId().equals(id)){
            throw new BusinessException("更新:数据库已有电话号!");
        }
    }
}




